/*!
 * QUnit 1.17.1
 * http://qunitjs.com/
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license
 * http://jquery.org/license
 *
 * Date: 2015-01-20T19:39Z
 */

(function( window ) {

	var QUnit,
		config,
		onErrorFnPrev,
		loggingCallbacks = {},
		fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ),
		toString = Object.prototype.toString,
		hasOwn = Object.prototype.hasOwnProperty,
	// Keep a local reference to Date (GH-283)
		Date = window.Date,
		now = Date.now || function() {
				return new Date().getTime();
			},
		globalStartCalled = false,
		runStarted = false,
		setTimeout = window.setTimeout,
		clearTimeout = window.clearTimeout,
		defined = {
			document: window.document !== undefined,
			setTimeout: window.setTimeout !== undefined,
			sessionStorage: (function() {
				var x = "qunit-test-string";
				try {
					sessionStorage.setItem( x, x );
					sessionStorage.removeItem( x );
					return true;
				} catch ( e ) {
					return false;
				}
			}())
		},
		/**
		 * Provides a normalized error string, correcting an issue
		 * with IE 7 (and prior) where Error.prototype.toString is
		 * not properly implemented
		 *
		 * Based on http://es5.github.com/#x15.11.4.4
		 *
		 * @param {String|Error} error
		 * @return {String} error message
		 */
		errorString = function( error ) {
			var name, message,
				errorString = error.toString();
			if ( errorString.substring( 0, 7 ) === "[object" ) {
				name = error.name ? error.name.toString() : "Error";
				message = error.message ? error.message.toString() : "";
				if ( name && message ) {
					return name + ": " + message;
				} else if ( name ) {
					return name;
				} else if ( message ) {
					return message;
				} else {
					return "Error";
				}
			} else {
				return errorString;
			}
		},
		/**
		 * Makes a clone of an object using only Array or Object as base,
		 * and copies over the own enumerable properties.
		 *
		 * @param {Object} obj
		 * @return {Object} New object with only the own properties (recursively).
		 */
		objectValues = function( obj ) {
			var key, val,
				vals = QUnit.is( "array", obj ) ? [] : {};
			for ( key in obj ) {
				if ( hasOwn.call( obj, key ) ) {
					val = obj[ key ];
					vals[ key ] = val === Object( val ) ? objectValues( val ) : val;
				}
			}
			return vals;
		};

	QUnit = {};

	/**
	 * Config object: Maintain internal state
	 * Later exposed as QUnit.config
	 * `config` initialized at top of scope
	 */
	config = {
		// The queue of tests to run
		queue: [],

		// block until document ready
		blocking: true,

		// by default, run previously failed tests first
		// very useful in combination with "Hide passed tests" checked
		reorder: true,

		// by default, modify document.title when suite is done
		altertitle: true,

		// by default, scroll to top of the page when suite is done
		scrolltop: true,

		// when enabled, all tests must call expect()
		requireExpects: false,

		// add checkboxes that are persisted in the query-string
		// when enabled, the id is set to `true` as a `QUnit.config` property
		urlConfig: [
			{
				id: "hidepassed",
				label: "Hide passed tests",
				tooltip: "Only show tests and assertions that fail. Stored as query-strings."
			},
			{
				id: "noglobals",
				label: "Check for Globals",
				tooltip: "Enabling this will test if any test introduces new properties on the " +
				"`window` object. Stored as query-strings."
			},
			{
				id: "notrycatch",
				label: "No try-catch",
				tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +
				"exceptions in IE reasonable. Stored as query-strings."
			}
		],

		// Set of all modules.
		modules: [],

		// The first unnamed module
		currentModule: {
			name: "",
			tests: []
		},

		callbacks: {}
	};

// Push a loose unnamed module to the modules collection
	config.modules.push( config.currentModule );

// Initialize more QUnit.config and QUnit.urlParams
	(function() {
		var i, current,
			location = window.location || { search: "", protocol: "file:" },
			params = location.search.slice( 1 ).split( "&" ),
			length = params.length,
			urlParams = {};

		if ( params[ 0 ] ) {
			for ( i = 0; i < length; i++ ) {
				current = params[ i ].split( "=" );
				current[ 0 ] = decodeURIComponent( current[ 0 ] );

				// allow just a key to turn on a flag, e.g., test.html?noglobals
				current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
				if ( urlParams[ current[ 0 ] ] ) {
					urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] );
				} else {
					urlParams[ current[ 0 ] ] = current[ 1 ];
				}
			}
		}

		if ( urlParams.filter === true ) {
			delete urlParams.filter;
		}

		QUnit.urlParams = urlParams;

		// String search anywhere in moduleName+testName
		config.filter = urlParams.filter;

		config.testId = [];
		if ( urlParams.testId ) {

			// Ensure that urlParams.testId is an array
			urlParams.testId = [].concat( urlParams.testId );
			for ( i = 0; i < urlParams.testId.length; i++ ) {
				config.testId.push( urlParams.testId[ i ] );
			}
		}

		// Figure out if we're running the tests from a server or not
		QUnit.isLocal = location.protocol === "file:";
	}());

// Root QUnit object.
// `QUnit` initialized at top of scope
	extend( QUnit, {

		// call on start of module test to prepend name to all tests
		module: function( name, testEnvironment ) {
			var currentModule = {
				name: name,
				testEnvironment: testEnvironment,
				tests: []
			};

			// DEPRECATED: handles setup/teardown functions,
			// beforeEach and afterEach should be used instead
			if ( testEnvironment && testEnvironment.setup ) {
				testEnvironment.beforeEach = testEnvironment.setup;
				delete testEnvironment.setup;
			}
			if ( testEnvironment && testEnvironment.teardown ) {
				testEnvironment.afterEach = testEnvironment.teardown;
				delete testEnvironment.teardown;
			}

			config.modules.push( currentModule );
			config.currentModule = currentModule;
		},

		// DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0.
		asyncTest: function( testName, expected, callback ) {
			if ( arguments.length === 2 ) {
				callback = expected;
				expected = null;
			}

			QUnit.test( testName, expected, callback, true );
		},

		test: function( testName, expected, callback, async ) {
			var test;

			if ( arguments.length === 2 ) {
				callback = expected;
				expected = null;
			}

			test = new Test({
				testName: testName,
				expected: expected,
				async: async,
				callback: callback
			});

			test.queue();
		},

		skip: function( testName ) {
			var test = new Test({
				testName: testName,
				skip: true
			});

			test.queue();
		},

		// DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0.
		// In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior.
		start: function( count ) {
			var globalStartAlreadyCalled = globalStartCalled;

			if ( !config.current ) {
				globalStartCalled = true;

				if ( runStarted ) {
					throw new Error( "Called start() outside of a test context while already started" );
				} else if ( globalStartAlreadyCalled || count > 1 ) {
					throw new Error( "Called start() outside of a test context too many times" );
				} else if ( config.autostart ) {
					throw new Error( "Called start() outside of a test context when " +
					"QUnit.config.autostart was true" );
				} else if ( !config.pageLoaded ) {

					// The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it
					config.autostart = true;
					return;
				}
			} else {

				// If a test is running, adjust its semaphore
				config.current.semaphore -= count || 1;

				// Don't start until equal number of stop-calls
				if ( config.current.semaphore > 0 ) {
					return;
				}

				// throw an Error if start is called more often than stop
				if ( config.current.semaphore < 0 ) {
					config.current.semaphore = 0;

					QUnit.pushFailure(
						"Called start() while already started (test's semaphore was 0 already)",
						sourceFromStacktrace( 2 )
					);
					return;
				}
			}

			resumeProcessing();
		},

		// DEPRECATED: QUnit.stop() will be removed in QUnit 2.0.
		stop: function( count ) {

			// If there isn't a test running, don't allow QUnit.stop() to be called
			if ( !config.current ) {
				throw new Error( "Called stop() outside of a test context" );
			}

			// If a test is running, adjust its semaphore
			config.current.semaphore += count || 1;

			pauseProcessing();
		},

		config: config,

		// Safe object type checking
		is: function( type, obj ) {
			return QUnit.objectType( obj ) === type;
		},

		objectType: function( obj ) {
			if ( typeof obj === "undefined" ) {
				return "undefined";
			}

			// Consider: typeof null === object
			if ( obj === null ) {
				return "null";
			}

			var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ),
				type = match && match[ 1 ] || "";

			switch ( type ) {
				case "Number":
					if ( isNaN( obj ) ) {
						return "nan";
					}
					return "number";
				case "String":
				case "Boolean":
				case "Array":
				case "Date":
				case "RegExp":
				case "Function":
					return type.toLowerCase();
			}
			if ( typeof obj === "object" ) {
				return "object";
			}
			return undefined;
		},

		extend: extend,

		load: function() {
			config.pageLoaded = true;

			// Initialize the configuration options
			extend( config, {
				stats: { all: 0, bad: 0 },
				moduleStats: { all: 0, bad: 0 },
				started: 0,
				updateRate: 1000,
				autostart: true,
				filter: ""
			}, true );

			config.blocking = false;

			if ( config.autostart ) {
				resumeProcessing();
			}
		}
	});

// Register logging callbacks
	(function() {
		var i, l, key,
			callbacks = [ "begin", "done", "log", "testStart", "testDone",
				"moduleStart", "moduleDone" ];

		function registerLoggingCallback( key ) {
			var loggingCallback = function( callback ) {
				if ( QUnit.objectType( callback ) !== "function" ) {
					throw new Error(
						"QUnit logging methods require a callback function as their first parameters."
					);
				}

				config.callbacks[ key ].push( callback );
			};

			// DEPRECATED: This will be removed on QUnit 2.0.0+
			// Stores the registered functions allowing restoring
			// at verifyLoggingCallbacks() if modified
			loggingCallbacks[ key ] = loggingCallback;

			return loggingCallback;
		}

		for ( i = 0, l = callbacks.length; i < l; i++ ) {
			key = callbacks[ i ];

			// Initialize key collection of logging callback
			if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) {
				config.callbacks[ key ] = [];
			}

			QUnit[ key ] = registerLoggingCallback( key );
		}
	})();

// `onErrorFnPrev` initialized at top of scope
// Preserve other handlers
	onErrorFnPrev = window.onerror;

// Cover uncaught exceptions
// Returning true will suppress the default browser handler,
// returning false will let it run.
	window.onerror = function( error, filePath, linerNr ) {
		var ret = false;
		if ( onErrorFnPrev ) {
			ret = onErrorFnPrev( error, filePath, linerNr );
		}

		// Treat return value as window.onerror itself does,
		// Only do our handling if not suppressed.
		if ( ret !== true ) {
			if ( QUnit.config.current ) {
				if ( QUnit.config.current.ignoreGlobalErrors ) {
					return true;
				}
				QUnit.pushFailure( error, filePath + ":" + linerNr );
			} else {
				QUnit.test( "global failure", extend(function() {
					QUnit.pushFailure( error, filePath + ":" + linerNr );
				}, { validTest: true } ) );
			}
			return false;
		}

		return ret;
	};

	function done() {
		var runtime, passed;

		config.autorun = true;

		// Log the last module results
		if ( config.previousModule ) {
			runLoggingCallbacks( "moduleDone", {
				name: config.previousModule.name,
				tests: config.previousModule.tests,
				failed: config.moduleStats.bad,
				passed: config.moduleStats.all - config.moduleStats.bad,
				total: config.moduleStats.all,
				runtime: now() - config.moduleStats.started
			});
		}
		delete config.previousModule;

		runtime = now() - config.started;
		passed = config.stats.all - config.stats.bad;

		runLoggingCallbacks( "done", {
			failed: config.stats.bad,
			passed: passed,
			total: config.stats.all,
			runtime: runtime
		});
	}

// Doesn't support IE6 to IE9
// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
	function extractStacktrace( e, offset ) {
		offset = offset === undefined ? 4 : offset;

		var stack, include, i;

		if ( e.stacktrace ) {

			// Opera 12.x
			return e.stacktrace.split( "\n" )[ offset + 3 ];
		} else if ( e.stack ) {

			// Firefox, Chrome, Safari 6+, IE10+, PhantomJS and Node
			stack = e.stack.split( "\n" );
			if ( /^error$/i.test( stack[ 0 ] ) ) {
				stack.shift();
			}
			if ( fileName ) {
				include = [];
				for ( i = offset; i < stack.length; i++ ) {
					if ( stack[ i ].indexOf( fileName ) !== -1 ) {
						break;
					}
					include.push( stack[ i ] );
				}
				if ( include.length ) {
					return include.join( "\n" );
				}
			}
			return stack[ offset ];
		} else if ( e.sourceURL ) {

			// Safari < 6
			// exclude useless self-reference for generated Error objects
			if ( /qunit.js$/.test( e.sourceURL ) ) {
				return;
			}

			// for actual exceptions, this is useful
			return e.sourceURL + ":" + e.line;
		}
	}

	function sourceFromStacktrace( offset ) {
		var e = new Error();
		if ( !e.stack ) {
			try {
				throw e;
			} catch ( err ) {
				// This should already be true in most browsers
				e = err;
			}
		}
		return extractStacktrace( e, offset );
	}

	function synchronize( callback, last ) {
		if ( QUnit.objectType( callback ) === "array" ) {
			while ( callback.length ) {
				synchronize( callback.shift() );
			}
			return;
		}
		config.queue.push( callback );

		if ( config.autorun && !config.blocking ) {
			process( last );
		}
	}

	function process( last ) {
		function next() {
			process( last );
		}
		var start = now();
		config.depth = ( config.depth || 0 ) + 1;

		while ( config.queue.length && !config.blocking ) {
			if ( !defined.setTimeout || config.updateRate <= 0 ||
				( ( now() - start ) < config.updateRate ) ) {
				if ( config.current ) {

					// Reset async tracking for each phase of the Test lifecycle
					config.current.usedAsync = false;
				}
				config.queue.shift()();
			} else {
				setTimeout( next, 13 );
				break;
			}
		}
		config.depth--;
		if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
			done();
		}
	}

	function begin() {
		var i, l,
			modulesLog = [];

		// If the test run hasn't officially begun yet
		if ( !config.started ) {

			// Record the time of the test run's beginning
			config.started = now();

			verifyLoggingCallbacks();

			// Delete the loose unnamed module if unused.
			if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {
				config.modules.shift();
			}

			// Avoid unnecessary information by not logging modules' test environments
			for ( i = 0, l = config.modules.length; i < l; i++ ) {
				modulesLog.push({
					name: config.modules[ i ].name,
					tests: config.modules[ i ].tests
				});
			}

			// The test run is officially beginning now
			runLoggingCallbacks( "begin", {
				totalTests: Test.count,
				modules: modulesLog
			});
		}

		config.blocking = false;
		process( true );
	}

	function resumeProcessing() {
		runStarted = true;

		// A slight delay to allow this iteration of the event loop to finish (more assertions, etc.)
		if ( defined.setTimeout ) {
			setTimeout(function() {
				if ( config.current && config.current.semaphore > 0 ) {
					return;
				}
				if ( config.timeout ) {
					clearTimeout( config.timeout );
				}

				begin();
			}, 13 );
		} else {
			begin();
		}
	}

	function pauseProcessing() {
		config.blocking = true;

		if ( config.testTimeout && defined.setTimeout ) {
			clearTimeout( config.timeout );
			config.timeout = setTimeout(function() {
				if ( config.current ) {
					config.current.semaphore = 0;
					QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );
				} else {
					throw new Error( "Test timed out" );
				}
				resumeProcessing();
			}, config.testTimeout );
		}
	}

	function saveGlobal() {
		config.pollution = [];

		if ( config.noglobals ) {
			for ( var key in window ) {
				if ( hasOwn.call( window, key ) ) {
					// in Opera sometimes DOM element ids show up here, ignore them
					if ( /^qunit-test-output/.test( key ) ) {
						continue;
					}
					config.pollution.push( key );
				}
			}
		}
	}

	function checkPollution() {
		var newGlobals,
			deletedGlobals,
			old = config.pollution;

		saveGlobal();

		newGlobals = diff( config.pollution, old );
		if ( newGlobals.length > 0 ) {
			QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );
		}

		deletedGlobals = diff( old, config.pollution );
		if ( deletedGlobals.length > 0 ) {
			QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) );
		}
	}

// returns a new Array with the elements that are in a but not in b
	function diff( a, b ) {
		var i, j,
			result = a.slice();

		for ( i = 0; i < result.length; i++ ) {
			for ( j = 0; j < b.length; j++ ) {
				if ( result[ i ] === b[ j ] ) {
					result.splice( i, 1 );
					i--;
					break;
				}
			}
		}
		return result;
	}

	function extend( a, b, undefOnly ) {
		for ( var prop in b ) {
			if ( hasOwn.call( b, prop ) ) {

				// Avoid "Member not found" error in IE8 caused by messing with window.constructor
				if ( !( prop === "constructor" && a === window ) ) {
					if ( b[ prop ] === undefined ) {
						delete a[ prop ];
					} else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) {
						a[ prop ] = b[ prop ];
					}
				}
			}
		}

		return a;
	}

	function runLoggingCallbacks( key, args ) {
		var i, l, callbacks;

		callbacks = config.callbacks[ key ];
		for ( i = 0, l = callbacks.length; i < l; i++ ) {
			callbacks[ i ]( args );
		}
	}

// DEPRECATED: This will be removed on 2.0.0+
// This function verifies if the loggingCallbacks were modified by the user
// If so, it will restore it, assign the given callback and print a console warning
	function verifyLoggingCallbacks() {
		var loggingCallback, userCallback;

		for ( loggingCallback in loggingCallbacks ) {
			if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) {

				userCallback = QUnit[ loggingCallback ];

				// Restore the callback function
				QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ];

				// Assign the deprecated given callback
				QUnit[ loggingCallback ]( userCallback );

				if ( window.console && window.console.warn ) {
					window.console.warn(
						"QUnit." + loggingCallback + " was replaced with a new value.\n" +
						"Please, check out the documentation on how to apply logging callbacks.\n" +
						"Reference: http://api.qunitjs.com/category/callbacks/"
					);
				}
			}
		}
	}

// from jquery.js
	function inArray( elem, array ) {
		if ( array.indexOf ) {
			return array.indexOf( elem );
		}

		for ( var i = 0, length = array.length; i < length; i++ ) {
			if ( array[ i ] === elem ) {
				return i;
			}
		}

		return -1;
	}

	function Test( settings ) {
		var i, l;

		++Test.count;

		extend( this, settings );
		this.assertions = [];
		this.semaphore = 0;
		this.usedAsync = false;
		this.module = config.currentModule;
		this.stack = sourceFromStacktrace( 3 );

		// Register unique strings
		for ( i = 0, l = this.module.tests; i < l.length; i++ ) {
			if ( this.module.tests[ i ].name === this.testName ) {
				this.testName += " ";
			}
		}

		this.testId = generateHash( this.module.name, this.testName );

		this.module.tests.push({
			name: this.testName,
			testId: this.testId
		});

		if ( settings.skip ) {

			// Skipped tests will fully ignore any sent callback
			this.callback = function() {};
			this.async = false;
			this.expected = 0;
		} else {
			this.assert = new Assert( this );
		}
	}

	Test.count = 0;

	Test.prototype = {
		before: function() {
			if (

				// Emit moduleStart when we're switching from one module to another
			this.module !== config.previousModule ||

				// They could be equal (both undefined) but if the previousModule property doesn't
				// yet exist it means this is the first test in a suite that isn't wrapped in a
				// module, in which case we'll just emit a moduleStart event for 'undefined'.
				// Without this, reporters can get testStart before moduleStart  which is a problem.
			!hasOwn.call( config, "previousModule" )
			) {
				if ( hasOwn.call( config, "previousModule" ) ) {
					runLoggingCallbacks( "moduleDone", {
						name: config.previousModule.name,
						tests: config.previousModule.tests,
						failed: config.moduleStats.bad,
						passed: config.moduleStats.all - config.moduleStats.bad,
						total: config.moduleStats.all,
						runtime: now() - config.moduleStats.started
					});
				}
				config.previousModule = this.module;
				config.moduleStats = { all: 0, bad: 0, started: now() };
				runLoggingCallbacks( "moduleStart", {
					name: this.module.name,
					tests: this.module.tests
				});
			}

			config.current = this;

			this.testEnvironment = extend( {}, this.module.testEnvironment );
			delete this.testEnvironment.beforeEach;
			delete this.testEnvironment.afterEach;

			this.started = now();
			runLoggingCallbacks( "testStart", {
				name: this.testName,
				module: this.module.name,
				testId: this.testId
			});

			if ( !config.pollution ) {
				saveGlobal();
			}
		},

		run: function() {
			var promise;

			config.current = this;

			if ( this.async ) {
				QUnit.stop();
			}

			this.callbackStarted = now();

			if ( config.notrycatch ) {
				promise = this.callback.call( this.testEnvironment, this.assert );
				this.resolvePromise( promise );
				return;
			}

			try {
				promise = this.callback.call( this.testEnvironment, this.assert );
				this.resolvePromise( promise );
			} catch ( e ) {
				this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " +
				this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );

				// else next test will carry the responsibility
				saveGlobal();

				// Restart the tests if they're blocking
				if ( config.blocking ) {
					QUnit.start();
				}
			}
		},

		after: function() {
			checkPollution();
		},

		queueHook: function( hook, hookName ) {
			var promise,
				test = this;
			return function runHook() {
				config.current = test;
				if ( config.notrycatch ) {
					promise = hook.call( test.testEnvironment, test.assert );
					test.resolvePromise( promise, hookName );
					return;
				}
				try {
					promise = hook.call( test.testEnvironment, test.assert );
					test.resolvePromise( promise, hookName );
				} catch ( error ) {
					test.pushFailure( hookName + " failed on " + test.testName + ": " +
					( error.message || error ), extractStacktrace( error, 0 ) );
				}
			};
		},

		// Currently only used for module level hooks, can be used to add global level ones
		hooks: function( handler ) {
			var hooks = [];

			// Hooks are ignored on skipped tests
			if ( this.skip ) {
				return hooks;
			}

			if ( this.module.testEnvironment &&
				QUnit.objectType( this.module.testEnvironment[ handler ] ) === "function" ) {
				hooks.push( this.queueHook( this.module.testEnvironment[ handler ], handler ) );
			}

			return hooks;
		},

		finish: function() {
			config.current = this;
			if ( config.requireExpects && this.expected === null ) {
				this.pushFailure( "Expected number of assertions to be defined, but expect() was " +
				"not called.", this.stack );
			} else if ( this.expected !== null && this.expected !== this.assertions.length ) {
				this.pushFailure( "Expected " + this.expected + " assertions, but " +
				this.assertions.length + " were run", this.stack );
			} else if ( this.expected === null && !this.assertions.length ) {
				this.pushFailure( "Expected at least one assertion, but none were run - call " +
				"expect(0) to accept zero assertions.", this.stack );
			}

			var i,
				bad = 0;

			this.runtime = now() - this.started;
			config.stats.all += this.assertions.length;
			config.moduleStats.all += this.assertions.length;

			for ( i = 0; i < this.assertions.length; i++ ) {
				if ( !this.assertions[ i ].result ) {
					bad++;
					config.stats.bad++;
					config.moduleStats.bad++;
				}
			}

			runLoggingCallbacks( "testDone", {
				name: this.testName,
				module: this.module.name,
				skipped: !!this.skip,
				failed: bad,
				passed: this.assertions.length - bad,
				total: this.assertions.length,
				runtime: this.runtime,

				// HTML Reporter use
				assertions: this.assertions,
				testId: this.testId,

				// DEPRECATED: this property will be removed in 2.0.0, use runtime instead
				duration: this.runtime
			});

			// QUnit.reset() is deprecated and will be replaced for a new
			// fixture reset function on QUnit 2.0/2.1.
			// It's still called here for backwards compatibility handling
			QUnit.reset();

			config.current = undefined;
		},

		queue: function() {
			var bad,
				test = this;

			if ( !this.valid() ) {
				return;
			}

			function run() {

				// each of these can by async
				synchronize([
					function() {
						test.before();
					},

					test.hooks( "beforeEach" ),

					function() {
						test.run();
					},

					test.hooks( "afterEach" ).reverse(),

					function() {
						test.after();
					},
					function() {
						test.finish();
					}
				]);
			}

			// `bad` initialized at top of scope
			// defer when previous test run passed, if storage is available
			bad = QUnit.config.reorder && defined.sessionStorage &&
			+sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );

			if ( bad ) {
				run();
			} else {
				synchronize( run, true );
			}
		},

		push: function( result, actual, expected, message ) {
			var source,
				details = {
					module: this.module.name,
					name: this.testName,
					result: result,
					message: message,
					actual: actual,
					expected: expected,
					testId: this.testId,
					runtime: now() - this.started
				};

			if ( !result ) {
				source = sourceFromStacktrace();

				if ( source ) {
					details.source = source;
				}
			}

			runLoggingCallbacks( "log", details );

			this.assertions.push({
				result: !!result,
				message: message
			});
		},

		pushFailure: function( message, source, actual ) {
			if ( !this instanceof Test ) {
				throw new Error( "pushFailure() assertion outside test context, was " +
				sourceFromStacktrace( 2 ) );
			}

			var details = {
				module: this.module.name,
				name: this.testName,
				result: false,
				message: message || "error",
				actual: actual || null,
				testId: this.testId,
				runtime: now() - this.started
			};

			if ( source ) {
				details.source = source;
			}

			runLoggingCallbacks( "log", details );

			this.assertions.push({
				result: false,
				message: message
			});
		},

		resolvePromise: function( promise, phase ) {
			var then, message,
				test = this;
			if ( promise != null ) {
				then = promise.then;
				if ( QUnit.objectType( then ) === "function" ) {
					QUnit.stop();
					then.call(
						promise,
						QUnit.start,
						function( error ) {
							message = "Promise rejected " +
							( !phase ? "during" : phase.replace( /Each$/, "" ) ) +
							" " + test.testName + ": " + ( error.message || error );
							test.pushFailure( message, extractStacktrace( error, 0 ) );

							// else next test will carry the responsibility
							saveGlobal();

							// Unblock
							QUnit.start();
						}
					);
				}
			}
		},

		valid: function() {
			var include,
				filter = config.filter,
				module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(),
				fullName = ( this.module.name + ": " + this.testName ).toLowerCase();

			// Internally-generated tests are always valid
			if ( this.callback && this.callback.validTest ) {
				return true;
			}

			if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) {
				return false;
			}

			if ( module && ( !this.module.name || this.module.name.toLowerCase() !== module ) ) {
				return false;
			}

			if ( !filter ) {
				return true;
			}

			include = filter.charAt( 0 ) !== "!";
			if ( !include ) {
				filter = filter.toLowerCase().slice( 1 );
			}

			// If the filter matches, we need to honour include
			if ( fullName.indexOf( filter ) !== -1 ) {
				return include;
			}

			// Otherwise, do the opposite
			return !include;
		}

	};

// Resets the test setup. Useful for tests that modify the DOM.
	/*
	 DEPRECATED: Use multiple tests instead of resetting inside a test.
	 Use testStart or testDone for custom cleanup.
	 This method will throw an error in 2.0, and will be removed in 2.1
	 */
	QUnit.reset = function() {

		// Return on non-browser environments
		// This is necessary to not break on node tests
		if ( typeof window === "undefined" ) {
			return;
		}

		var fixture = defined.document && document.getElementById &&
			document.getElementById( "qunit-fixture" );

		if ( fixture ) {
			fixture.innerHTML = config.fixture;
		}
	};

	QUnit.pushFailure = function() {
		if ( !QUnit.config.current ) {
			throw new Error( "pushFailure() assertion outside test context, in " +
			sourceFromStacktrace( 2 ) );
		}

		// Gets current test obj
		var currentTest = QUnit.config.current;

		return currentTest.pushFailure.apply( currentTest, arguments );
	};

// Based on Java's String.hashCode, a simple but not
// rigorously collision resistant hashing function
	function generateHash( module, testName ) {
		var hex,
			i = 0,
			hash = 0,
			str = module + "\x1C" + testName,
			len = str.length;

		for ( ; i < len; i++ ) {
			hash  = ( ( hash << 5 ) - hash ) + str.charCodeAt( i );
			hash |= 0;
		}

		// Convert the possibly negative integer hash code into an 8 character hex string, which isn't
		// strictly necessary but increases user understanding that the id is a SHA-like hash
		hex = ( 0x100000000 + hash ).toString( 16 );
		if ( hex.length < 8 ) {
			hex = "0000000" + hex;
		}

		return hex.slice( -8 );
	}

	function Assert( testContext ) {
		this.test = testContext;
	}

// Assert helpers
	QUnit.assert = Assert.prototype = {

		// Specify the number of expected assertions to guarantee that failed test
		// (no assertions are run at all) don't slip through.
		expect: function( asserts ) {
			if ( arguments.length === 1 ) {
				this.test.expected = asserts;
			} else {
				return this.test.expected;
			}
		},

		// Increment this Test's semaphore counter, then return a single-use function that
		// decrements that counter a maximum of once.
		async: function() {
			var test = this.test,
				popped = false;

			test.semaphore += 1;
			test.usedAsync = true;
			pauseProcessing();

			return function done() {
				if ( !popped ) {
					test.semaphore -= 1;
					popped = true;
					resumeProcessing();
				} else {
					test.pushFailure( "Called the callback returned from `assert.async` more than once",
						sourceFromStacktrace( 2 ) );
				}
			};
		},

		// Exports test.push() to the user API
		push: function( /* result, actual, expected, message */ ) {
			var assert = this,
				currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;

			// Backwards compatibility fix.
			// Allows the direct use of global exported assertions and QUnit.assert.*
			// Although, it's use is not recommended as it can leak assertions
			// to other tests from async tests, because we only get a reference to the current test,
			// not exactly the test where assertion were intended to be called.
			if ( !currentTest ) {
				throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) );
			}

			if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) {
				currentTest.pushFailure( "Assertion after the final `assert.async` was resolved",
					sourceFromStacktrace( 2 ) );

				// Allow this assertion to continue running anyway...
			}

			if ( !( assert instanceof Assert ) ) {
				assert = currentTest.assert;
			}
			return assert.test.push.apply( assert.test, arguments );
		},

		/**
		 * Asserts rough true-ish result.
		 * @name ok
		 * @function
		 * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
		 */
		ok: function( result, message ) {
			message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
			QUnit.dump.parse( result ) );
			this.push( !!result, result, true, message );
		},

		/**
		 * Assert that the first two arguments are equal, with an optional message.
		 * Prints out both actual and expected values.
		 * @name equal
		 * @function
		 * @example equal( format( "{0} bytes.", 2), "2 bytes.", "replaces {0} with next argument" );
		 */
		equal: function( actual, expected, message ) {
			/*jshint eqeqeq:false */
			this.push( expected == actual, actual, expected, message );
		},

		/**
		 * @name notEqual
		 * @function
		 */
		notEqual: function( actual, expected, message ) {
			/*jshint eqeqeq:false */
			this.push( expected != actual, actual, expected, message );
		},

		/**
		 * @name propEqual
		 * @function
		 */
		propEqual: function( actual, expected, message ) {
			actual = objectValues( actual );
			expected = objectValues( expected );
			this.push( QUnit.equiv( actual, expected ), actual, expected, message );
		},

		/**
		 * @name notPropEqual
		 * @function
		 */
		notPropEqual: function( actual, expected, message ) {
			actual = objectValues( actual );
			expected = objectValues( expected );
			this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
		},

		/**
		 * @name deepEqual
		 * @function
		 */
		deepEqual: function( actual, expected, message ) {
			this.push( QUnit.equiv( actual, expected ), actual, expected, message );
		},

		/**
		 * @name notDeepEqual
		 * @function
		 */
		notDeepEqual: function( actual, expected, message ) {
			this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
		},

		/**
		 * @name strictEqual
		 * @function
		 */
		strictEqual: function( actual, expected, message ) {
			this.push( expected === actual, actual, expected, message );
		},

		/**
		 * @name notStrictEqual
		 * @function
		 */
		notStrictEqual: function( actual, expected, message ) {
			this.push( expected !== actual, actual, expected, message );
		},

		"throws": function( block, expected, message ) {
			var actual, expectedType,
				expectedOutput = expected,
				ok = false;

			// 'expected' is optional unless doing string comparison
			if ( message == null && typeof expected === "string" ) {
				message = expected;
				expected = null;
			}

			this.test.ignoreGlobalErrors = true;
			try {
				block.call( this.test.testEnvironment );
			} catch (e) {
				actual = e;
			}
			this.test.ignoreGlobalErrors = false;

			if ( actual ) {
				expectedType = QUnit.objectType( expected );

				// we don't want to validate thrown error
				if ( !expected ) {
					ok = true;
					expectedOutput = null;

					// expected is a regexp
				} else if ( expectedType === "regexp" ) {
					ok = expected.test( errorString( actual ) );

					// expected is a string
				} else if ( expectedType === "string" ) {
					ok = expected === errorString( actual );

					// expected is a constructor, maybe an Error constructor
				} else if ( expectedType === "function" && actual instanceof expected ) {
					ok = true;

					// expected is an Error object
				} else if ( expectedType === "object" ) {
					ok = actual instanceof expected.constructor &&
					actual.name === expected.name &&
					actual.message === expected.message;

					// expected is a validation function which returns true if validation passed
				} else if ( expectedType === "function" && expected.call( {}, actual ) === true ) {
					expectedOutput = null;
					ok = true;
				}

				this.push( ok, actual, expectedOutput, message );
			} else {
				this.test.pushFailure( message, null, "No exception was thrown." );
			}
		}
	};

// Provide an alternative to assert.throws(), for enviroments that consider throws a reserved word
// Known to us are: Closure Compiler, Narwhal
	(function() {
		/*jshint sub:true */
		Assert.prototype.raises = Assert.prototype[ "throws" ];
	}());

// Test for equality any JavaScript type.
// Author: Philippe RathÃ© <prathe@gmail.com>
	QUnit.equiv = (function() {

		// Call the o related callback with the given arguments.
		function bindCallbacks( o, callbacks, args ) {
			var prop = QUnit.objectType( o );
			if ( prop ) {
				if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
					return callbacks[ prop ].apply( callbacks, args );
				} else {
					return callbacks[ prop ]; // or undefined
				}
			}
		}

		// the real equiv function
		var innerEquiv,

		// stack to decide between skip/abort functions
			callers = [],

		// stack to avoiding loops from circular referencing
			parents = [],
			parentsB = [],

			getProto = Object.getPrototypeOf || function( obj ) {
					/* jshint camelcase: false, proto: true */
					return obj.__proto__;
				},
			callbacks = (function() {

				// for string, boolean, number and null
				function useStrictEquality( b, a ) {

					/*jshint eqeqeq:false */
					if ( b instanceof a.constructor || a instanceof b.constructor ) {

						// to catch short annotation VS 'new' annotation of a
						// declaration
						// e.g. var i = 1;
						// var j = new Number(1);
						return a == b;
					} else {
						return a === b;
					}
				}

				return {
					"string": useStrictEquality,
					"boolean": useStrictEquality,
					"number": useStrictEquality,
					"null": useStrictEquality,
					"undefined": useStrictEquality,

					"nan": function( b ) {
						return isNaN( b );
					},

					"date": function( b, a ) {
						return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
					},

					"regexp": function( b, a ) {
						return QUnit.objectType( b ) === "regexp" &&

								// the regex itself
							a.source === b.source &&

								// and its modifiers
							a.global === b.global &&

								// (gmi) ...
							a.ignoreCase === b.ignoreCase &&
							a.multiline === b.multiline &&
							a.sticky === b.sticky;
					},

					// - skip when the property is a method of an instance (OOP)
					// - abort otherwise,
					// initial === would have catch identical references anyway
					"function": function() {
						var caller = callers[ callers.length - 1 ];
						return caller !== Object && typeof caller !== "undefined";
					},

					"array": function( b, a ) {
						var i, j, len, loop, aCircular, bCircular;

						// b could be an object literal here
						if ( QUnit.objectType( b ) !== "array" ) {
							return false;
						}

						len = a.length;
						if ( len !== b.length ) {
							// safe and faster
							return false;
						}

						// track reference to avoid circular references
						parents.push( a );
						parentsB.push( b );
						for ( i = 0; i < len; i++ ) {
							loop = false;
							for ( j = 0; j < parents.length; j++ ) {
								aCircular = parents[ j ] === a[ i ];
								bCircular = parentsB[ j ] === b[ i ];
								if ( aCircular || bCircular ) {
									if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
										loop = true;
									} else {
										parents.pop();
										parentsB.pop();
										return false;
									}
								}
							}
							if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
								parents.pop();
								parentsB.pop();
								return false;
							}
						}
						parents.pop();
						parentsB.pop();
						return true;
					},

					"object": function( b, a ) {

						/*jshint forin:false */
						var i, j, loop, aCircular, bCircular,
						// Default to true
							eq = true,
							aProperties = [],
							bProperties = [];

						// comparing constructors is more strict than using
						// instanceof
						if ( a.constructor !== b.constructor ) {

							// Allow objects with no prototype to be equivalent to
							// objects with Object as their constructor.
							if ( !( ( getProto( a ) === null && getProto( b ) === Object.prototype ) ||
								( getProto( b ) === null && getProto( a ) === Object.prototype ) ) ) {
								return false;
							}
						}

						// stack constructor before traversing properties
						callers.push( a.constructor );

						// track reference to avoid circular references
						parents.push( a );
						parentsB.push( b );

						// be strict: don't ensure hasOwnProperty and go deep
						for ( i in a ) {
							loop = false;
							for ( j = 0; j < parents.length; j++ ) {
								aCircular = parents[ j ] === a[ i ];
								bCircular = parentsB[ j ] === b[ i ];
								if ( aCircular || bCircular ) {
									if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
										loop = true;
									} else {
										eq = false;
										break;
									}
								}
							}
							aProperties.push( i );
							if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
								eq = false;
								break;
							}
						}

						parents.pop();
						parentsB.pop();
						callers.pop(); // unstack, we are done

						for ( i in b ) {
							bProperties.push( i ); // collect b's properties
						}

						// Ensures identical properties name
						return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
					}
				};
			}());

		innerEquiv = function() { // can take multiple arguments
			var args = [].slice.apply( arguments );
			if ( args.length < 2 ) {
				return true; // end transition
			}

			return ( (function( a, b ) {
				if ( a === b ) {
					return true; // catch the most you can
				} else if ( a === null || b === null || typeof a === "undefined" ||
					typeof b === "undefined" ||
					QUnit.objectType( a ) !== QUnit.objectType( b ) ) {

					// don't lose time with error prone cases
					return false;
				} else {
					return bindCallbacks( a, callbacks, [ b, a ] );
				}

				// apply transition with (1..n) arguments
			}( args[ 0 ], args[ 1 ] ) ) &&
			innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) );
		};

		return innerEquiv;
	}());

// Based on jsDump by Ariel Flesler
// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
	QUnit.dump = (function() {
		function quote( str ) {
			return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
		}
		function literal( o ) {
			return o + "";
		}
		function join( pre, arr, post ) {
			var s = dump.separator(),
				base = dump.indent(),
				inner = dump.indent( 1 );
			if ( arr.join ) {
				arr = arr.join( "," + s + inner );
			}
			if ( !arr ) {
				return pre + post;
			}
			return [ pre, inner + arr, base + post ].join( s );
		}
		function array( arr, stack ) {
			var i = arr.length,
				ret = new Array( i );

			if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
				return "[object Array]";
			}

			this.up();
			while ( i-- ) {
				ret[ i ] = this.parse( arr[ i ], undefined, stack );
			}
			this.down();
			return join( "[", ret, "]" );
		}

		var reName = /^function (\w+)/,
			dump = {

				// objType is used mostly internally, you can fix a (custom) type in advance
				parse: function( obj, objType, stack ) {
					stack = stack || [];
					var res, parser, parserType,
						inStack = inArray( obj, stack );

					if ( inStack !== -1 ) {
						return "recursion(" + ( inStack - stack.length ) + ")";
					}

					objType = objType || this.typeOf( obj  );
					parser = this.parsers[ objType ];
					parserType = typeof parser;

					if ( parserType === "function" ) {
						stack.push( obj );
						res = parser.call( this, obj, stack );
						stack.pop();
						return res;
					}
					return ( parserType === "string" ) ? parser : this.parsers.error;
				},
				typeOf: function( obj ) {
					var type;
					if ( obj === null ) {
						type = "null";
					} else if ( typeof obj === "undefined" ) {
						type = "undefined";
					} else if ( QUnit.is( "regexp", obj ) ) {
						type = "regexp";
					} else if ( QUnit.is( "date", obj ) ) {
						type = "date";
					} else if ( QUnit.is( "function", obj ) ) {
						type = "function";
					} else if ( obj.setInterval !== undefined &&
						obj.document !== undefined &&
						obj.nodeType === undefined ) {
						type = "window";
					} else if ( obj.nodeType === 9 ) {
						type = "document";
					} else if ( obj.nodeType ) {
						type = "node";
					} else if (

						// native arrays
					toString.call( obj ) === "[object Array]" ||

						// NodeList objects
					( typeof obj.length === "number" && obj.item !== undefined &&
					( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null &&
					obj[ 0 ] === undefined ) ) )
					) {
						type = "array";
					} else if ( obj.constructor === Error.prototype.constructor ) {
						type = "error";
					} else {
						type = typeof obj;
					}
					return type;
				},
				separator: function() {
					return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&#160;" : " ";
				},
				// extra can be a number, shortcut for increasing-calling-decreasing
				indent: function( extra ) {
					if ( !this.multiline ) {
						return "";
					}
					var chr = this.indentChar;
					if ( this.HTML ) {
						chr = chr.replace( /\t/g, "   " ).replace( / /g, "&#160;" );
					}
					return new Array( this.depth + ( extra || 0 ) ).join( chr );
				},
				up: function( a ) {
					this.depth += a || 1;
				},
				down: function( a ) {
					this.depth -= a || 1;
				},
				setParser: function( name, parser ) {
					this.parsers[ name ] = parser;
				},
				// The next 3 are exposed so you can use them
				quote: quote,
				literal: literal,
				join: join,
				//
				depth: 1,
				maxDepth: 5,

				// This is the list of parsers, to modify them, use dump.setParser
				parsers: {
					window: "[Window]",
					document: "[Document]",
					error: function( error ) {
						return "Error(\"" + error.message + "\")";
					},
					unknown: "[Unknown]",
					"null": "null",
					"undefined": "undefined",
					"function": function( fn ) {
						var ret = "function",

						// functions never have name in IE
							name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];

						if ( name ) {
							ret += " " + name;
						}
						ret += "( ";

						ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" );
						return join( ret, dump.parse( fn, "functionCode" ), "}" );
					},
					array: array,
					nodelist: array,
					"arguments": array,
					object: function( map, stack ) {
						var keys, key, val, i, nonEnumerableProperties,
							ret = [];

						if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
							return "[object Object]";
						}

						dump.up();
						keys = [];
						for ( key in map ) {
							keys.push( key );
						}

						// Some properties are not always enumerable on Error objects.
						nonEnumerableProperties = [ "message", "name" ];
						for ( i in nonEnumerableProperties ) {
							key = nonEnumerableProperties[ i ];
							if ( key in map && !( key in keys ) ) {
								keys.push( key );
							}
						}
						keys.sort();
						for ( i = 0; i < keys.length; i++ ) {
							key = keys[ i ];
							val = map[ key ];
							ret.push( dump.parse( key, "key" ) + ": " +
							dump.parse( val, undefined, stack ) );
						}
						dump.down();
						return join( "{", ret, "}" );
					},
					node: function( node ) {
						var len, i, val,
							open = dump.HTML ? "&lt;" : "<",
							close = dump.HTML ? "&gt;" : ">",
							tag = node.nodeName.toLowerCase(),
							ret = open + tag,
							attrs = node.attributes;

						if ( attrs ) {
							for ( i = 0, len = attrs.length; i < len; i++ ) {
								val = attrs[ i ].nodeValue;

								// IE6 includes all attributes in .attributes, even ones not explicitly
								// set. Those have values like undefined, null, 0, false, "" or
								// "inherit".
								if ( val && val !== "inherit" ) {
									ret += " " + attrs[ i ].nodeName + "=" +
									dump.parse( val, "attribute" );
								}
							}
						}
						ret += close;

						// Show content of TextNode or CDATASection
						if ( node.nodeType === 3 || node.nodeType === 4 ) {
							ret += node.nodeValue;
						}

						return ret + open + "/" + tag + close;
					},

					// function calls it internally, it's the arguments part of the function
					functionArgs: function( fn ) {
						var args,
							l = fn.length;

						if ( !l ) {
							return "";
						}

						args = new Array( l );
						while ( l-- ) {

							// 97 is 'a'
							args[ l ] = String.fromCharCode( 97 + l );
						}
						return " " + args.join( ", " ) + " ";
					},
					// object calls it internally, the key part of an item in a map
					key: quote,
					// function calls it internally, it's the content of the function
					functionCode: "[code]",
					// node calls it internally, it's an html attribute value
					attribute: quote,
					string: quote,
					date: quote,
					regexp: literal,
					number: literal,
					"boolean": literal
				},
				// if true, entities are escaped ( <, >, \t, space and \n )
				HTML: false,
				// indentation unit
				indentChar: "  ",
				// if true, items in a collection, are separated by a \n, else just a space.
				multiline: true
			};

		return dump;
	}());

// back compat
	QUnit.jsDump = QUnit.dump;

// For browser, export only select globals
	if ( typeof window !== "undefined" ) {

		// Deprecated
		// Extend assert methods to QUnit and Global scope through Backwards compatibility
		(function() {
			var i,
				assertions = Assert.prototype;

			function applyCurrent( current ) {
				return function() {
					var assert = new Assert( QUnit.config.current );
					current.apply( assert, arguments );
				};
			}

			for ( i in assertions ) {
				QUnit[ i ] = applyCurrent( assertions[ i ] );
			}
		})();

		(function() {
			var i, l,
				keys = [
					"test",
					"module",
					"expect",
					"asyncTest",
					"start",
					"stop",
					"ok",
					"equal",
					"notEqual",
					"propEqual",
					"notPropEqual",
					"deepEqual",
					"notDeepEqual",
					"strictEqual",
					"notStrictEqual",
					"throws"
				];

			for ( i = 0, l = keys.length; i < l; i++ ) {
				window[ keys[ i ] ] = QUnit[ keys[ i ] ];
			}
		})();

		window.QUnit = QUnit;
	}

// For nodejs
	if ( typeof module !== "undefined" && module && module.exports ) {
		module.exports = QUnit;

		// For consistency with CommonJS environments' exports
		module.exports.QUnit = QUnit;
	}

// For CommonJS with exports, but without module.exports, like Rhino
	if ( typeof exports !== "undefined" && exports ) {
		exports.QUnit = QUnit;
	}

// Get a reference to the global object, like window in browsers
}( (function() {
	return this;
})() ));

/*istanbul ignore next */
// jscs:disable maximumLineLength
/*
 * Javascript Diff Algorithm
 *  By John Resig (http://ejohn.org/)
 *  Modified by Chu Alan "sprite"
 *
 * Released under the MIT license.
 *
 * More Info:
 *  http://ejohn.org/projects/javascript-diff-algorithm/
 *
 * Usage: QUnit.diff(expected, actual)
 *
 * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the  quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
 */
QUnit.diff = (function() {
	var hasOwn = Object.prototype.hasOwnProperty;

	/*jshint eqeqeq:false, eqnull:true */
	function diff( o, n ) {
		var i,
			ns = {},
			os = {};

		for ( i = 0; i < n.length; i++ ) {
			if ( !hasOwn.call( ns, n[ i ] ) ) {
				ns[ n[ i ] ] = {
					rows: [],
					o: null
				};
			}
			ns[ n[ i ] ].rows.push( i );
		}

		for ( i = 0; i < o.length; i++ ) {
			if ( !hasOwn.call( os, o[ i ] ) ) {
				os[ o[ i ] ] = {
					rows: [],
					n: null
				};
			}
			os[ o[ i ] ].rows.push( i );
		}

		for ( i in ns ) {
			if ( hasOwn.call( ns, i ) ) {
				if ( ns[ i ].rows.length === 1 && hasOwn.call( os, i ) && os[ i ].rows.length === 1 ) {
					n[ ns[ i ].rows[ 0 ] ] = {
						text: n[ ns[ i ].rows[ 0 ] ],
						row: os[ i ].rows[ 0 ]
					};
					o[ os[ i ].rows[ 0 ] ] = {
						text: o[ os[ i ].rows[ 0 ] ],
						row: ns[ i ].rows[ 0 ]
					};
				}
			}
		}

		for ( i = 0; i < n.length - 1; i++ ) {
			if ( n[ i ].text != null && n[ i + 1 ].text == null && n[ i ].row + 1 < o.length && o[ n[ i ].row + 1 ].text == null &&
				n[ i + 1 ] == o[ n[ i ].row + 1 ] ) {

				n[ i + 1 ] = {
					text: n[ i + 1 ],
					row: n[ i ].row + 1
				};
				o[ n[ i ].row + 1 ] = {
					text: o[ n[ i ].row + 1 ],
					row: i + 1
				};
			}
		}

		for ( i = n.length - 1; i > 0; i-- ) {
			if ( n[ i ].text != null && n[ i - 1 ].text == null && n[ i ].row > 0 && o[ n[ i ].row - 1 ].text == null &&
				n[ i - 1 ] == o[ n[ i ].row - 1 ] ) {

				n[ i - 1 ] = {
					text: n[ i - 1 ],
					row: n[ i ].row - 1
				};
				o[ n[ i ].row - 1 ] = {
					text: o[ n[ i ].row - 1 ],
					row: i - 1
				};
			}
		}

		return {
			o: o,
			n: n
		};
	}

	return function( o, n ) {
		o = o.replace( /\s+$/, "" );
		n = n.replace( /\s+$/, "" );

		var i, pre,
			str = "",
			out = diff( o === "" ? [] : o.split( /\s+/ ), n === "" ? [] : n.split( /\s+/ ) ),
			oSpace = o.match( /\s+/g ),
			nSpace = n.match( /\s+/g );

		if ( oSpace == null ) {
			oSpace = [ " " ];
		} else {
			oSpace.push( " " );
		}

		if ( nSpace == null ) {
			nSpace = [ " " ];
		} else {
			nSpace.push( " " );
		}

		if ( out.n.length === 0 ) {
			for ( i = 0; i < out.o.length; i++ ) {
				str += "<del>" + out.o[ i ] + oSpace[ i ] + "</del>";
			}
		} else {
			if ( out.n[ 0 ].text == null ) {
				for ( n = 0; n < out.o.length && out.o[ n ].text == null; n++ ) {
					str += "<del>" + out.o[ n ] + oSpace[ n ] + "</del>";
				}
			}

			for ( i = 0; i < out.n.length; i++ ) {
				if ( out.n[ i ].text == null ) {
					str += "<ins>" + out.n[ i ] + nSpace[ i ] + "</ins>";
				} else {

					// `pre` initialized at top of scope
					pre = "";

					for ( n = out.n[ i ].row + 1; n < out.o.length && out.o[ n ].text == null; n++ ) {
						pre += "<del>" + out.o[ n ] + oSpace[ n ] + "</del>";
					}
					str += " " + out.n[ i ].text + nSpace[ i ] + pre;
				}
			}
		}

		return str;
	};
}());
// jscs:enable

(function() {

// Deprecated QUnit.init - Ref #530
// Re-initialize the configuration options
	QUnit.init = function() {
		var tests, banner, result, qunit,
			config = QUnit.config;

		config.stats = { all: 0, bad: 0 };
		config.moduleStats = { all: 0, bad: 0 };
		config.started = 0;
		config.updateRate = 1000;
		config.blocking = false;
		config.autostart = true;
		config.autorun = false;
		config.filter = "";
		config.queue = [];

		// Return on non-browser environments
		// This is necessary to not break on node tests
		if ( typeof window === "undefined" ) {
			return;
		}

		qunit = id( "qunit" );
		if ( qunit ) {
			qunit.innerHTML =
				"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
				"<h2 id='qunit-banner'></h2>" +
				"<div id='qunit-testrunner-toolbar'></div>" +
				"<h2 id='qunit-userAgent'></h2>" +
				"<ol id='qunit-tests'></ol>";
		}

		tests = id( "qunit-tests" );
		banner = id( "qunit-banner" );
		result = id( "qunit-testresult" );

		if ( tests ) {
			tests.innerHTML = "";
		}

		if ( banner ) {
			banner.className = "";
		}

		if ( result ) {
			result.parentNode.removeChild( result );
		}

		if ( tests ) {
			result = document.createElement( "p" );
			result.id = "qunit-testresult";
			result.className = "result";
			tests.parentNode.insertBefore( result, tests );
			result.innerHTML = "Running...<br />&#160;";
		}
	};

// Don't load the HTML Reporter on non-Browser environments
	if ( typeof window === "undefined" ) {
		return;
	}

	var config = QUnit.config,
		hasOwn = Object.prototype.hasOwnProperty,
		defined = {
			document: window.document !== undefined,
			sessionStorage: (function() {
				var x = "qunit-test-string";
				try {
					sessionStorage.setItem( x, x );
					sessionStorage.removeItem( x );
					return true;
				} catch ( e ) {
					return false;
				}
			}())
		},
		modulesList = [];

	/**
	 * Escape text for attribute or text content.
	 */
	function escapeText( s ) {
		if ( !s ) {
			return "";
		}
		s = s + "";

		// Both single quotes and double quotes (for attributes)
		return s.replace( /['"<>&]/g, function( s ) {
			switch ( s ) {
				case "'":
					return "&#039;";
				case "\"":
					return "&quot;";
				case "<":
					return "&lt;";
				case ">":
					return "&gt;";
				case "&":
					return "&amp;";
			}
		});
	}

	/**
	 * @param {HTMLElement} elem
	 * @param {string} type
	 * @param {Function} fn
	 */
	function addEvent( elem, type, fn ) {
		if ( elem.addEventListener ) {

			// Standards-based browsers
			elem.addEventListener( type, fn, false );
		} else if ( elem.attachEvent ) {

			// support: IE <9
			elem.attachEvent( "on" + type, fn );
		}
	}

	/**
	 * @param {Array|NodeList} elems
	 * @param {string} type
	 * @param {Function} fn
	 */
	function addEvents( elems, type, fn ) {
		var i = elems.length;
		while ( i-- ) {
			addEvent( elems[ i ], type, fn );
		}
	}

	function hasClass( elem, name ) {
		return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;
	}

	function addClass( elem, name ) {
		if ( !hasClass( elem, name ) ) {
			elem.className += ( elem.className ? " " : "" ) + name;
		}
	}

	function toggleClass( elem, name ) {
		if ( hasClass( elem, name ) ) {
			removeClass( elem, name );
		} else {
			addClass( elem, name );
		}
	}

	function removeClass( elem, name ) {
		var set = " " + elem.className + " ";

		// Class name may appear multiple times
		while ( set.indexOf( " " + name + " " ) >= 0 ) {
			set = set.replace( " " + name + " ", " " );
		}

		// trim for prettiness
		elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" );
	}

	function id( name ) {
		return defined.document && document.getElementById && document.getElementById( name );
	}

	function getUrlConfigHtml() {
		var i, j, val,
			escaped, escapedTooltip,
			selection = false,
			len = config.urlConfig.length,
			urlConfigHtml = "";

		for ( i = 0; i < len; i++ ) {
			val = config.urlConfig[ i ];
			if ( typeof val === "string" ) {
				val = {
					id: val,
					label: val
				};
			}

			escaped = escapeText( val.id );
			escapedTooltip = escapeText( val.tooltip );

			if ( config[ val.id ] === undefined ) {
				config[ val.id ] = QUnit.urlParams[ val.id ];
			}

			if ( !val.value || typeof val.value === "string" ) {
				urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
				"' name='" + escaped + "' type='checkbox'" +
				( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
				( config[ val.id ] ? " checked='checked'" : "" ) +
				" title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +
				"' title='" + escapedTooltip + "'>" + val.label + "</label>";
			} else {
				urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
				"' title='" + escapedTooltip + "'>" + val.label +
				": </label><select id='qunit-urlconfig-" + escaped +
				"' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";

				if ( QUnit.is( "array", val.value ) ) {
					for ( j = 0; j < val.value.length; j++ ) {
						escaped = escapeText( val.value[ j ] );
						urlConfigHtml += "<option value='" + escaped + "'" +
						( config[ val.id ] === val.value[ j ] ?
						( selection = true ) && " selected='selected'" : "" ) +
						">" + escaped + "</option>";
					}
				} else {
					for ( j in val.value ) {
						if ( hasOwn.call( val.value, j ) ) {
							urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
							( config[ val.id ] === j ?
							( selection = true ) && " selected='selected'" : "" ) +
							">" + escapeText( val.value[ j ] ) + "</option>";
						}
					}
				}
				if ( config[ val.id ] && !selection ) {
					escaped = escapeText( config[ val.id ] );
					urlConfigHtml += "<option value='" + escaped +
					"' selected='selected' disabled='disabled'>" + escaped + "</option>";
				}
				urlConfigHtml += "</select>";
			}
		}

		return urlConfigHtml;
	}

// Handle "click" events on toolbar checkboxes and "change" for select menus.
// Updates the URL with the new state of `config.urlConfig` values.
	function toolbarChanged() {
		var updatedUrl, value,
			field = this,
			params = {};

		// Detect if field is a select menu or a checkbox
		if ( "selectedIndex" in field ) {
			value = field.options[ field.selectedIndex ].value || undefined;
		} else {
			value = field.checked ? ( field.defaultValue || true ) : undefined;
		}

		params[ field.name ] = value;
		updatedUrl = setUrl( params );

		if ( "hidepassed" === field.name && "replaceState" in window.history ) {
			config[ field.name ] = value || false;
			if ( value ) {
				addClass( id( "qunit-tests" ), "hidepass" );
			} else {
				removeClass( id( "qunit-tests" ), "hidepass" );
			}

			// It is not necessary to refresh the whole page
			window.history.replaceState( null, "", updatedUrl );
		} else {
			window.location = updatedUrl;
		}
	}

	function setUrl( params ) {
		var key,
			querystring = "?";

		params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params );

		for ( key in params ) {
			if ( hasOwn.call( params, key ) ) {
				if ( params[ key ] === undefined ) {
					continue;
				}
				querystring += encodeURIComponent( key );
				if ( params[ key ] !== true ) {
					querystring += "=" + encodeURIComponent( params[ key ] );
				}
				querystring += "&";
			}
		}
		return location.protocol + "//" + location.host +
			location.pathname + querystring.slice( 0, -1 );
	}

	function applyUrlParams() {
		var selectBox = id( "qunit-modulefilter" ),
			selection = decodeURIComponent( selectBox.options[ selectBox.selectedIndex ].value ),
			filter = id( "qunit-filter-input" ).value;

		window.location = setUrl({
			module: ( selection === "" ) ? undefined : selection,
			filter: ( filter === "" ) ? undefined : filter,

			// Remove testId filter
			testId: undefined
		});
	}

	function toolbarUrlConfigContainer() {
		var urlConfigContainer = document.createElement( "span" );

		urlConfigContainer.innerHTML = getUrlConfigHtml();
		addClass( urlConfigContainer, "qunit-url-config" );

		// For oldIE support:
		// * Add handlers to the individual elements instead of the container
		// * Use "click" instead of "change" for checkboxes
		addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );
		addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );

		return urlConfigContainer;
	}

	function toolbarLooseFilter() {
		var filter = document.createElement( "form" ),
			label = document.createElement( "label" ),
			input = document.createElement( "input" ),
			button = document.createElement( "button" );

		addClass( filter, "qunit-filter" );

		label.innerHTML = "Filter: ";

		input.type = "text";
		input.value = config.filter || "";
		input.name = "filter";
		input.id = "qunit-filter-input";

		button.innerHTML = "Go";

		label.appendChild( input );

		filter.appendChild( label );
		filter.appendChild( button );
		addEvent( filter, "submit", function( ev ) {
			applyUrlParams();

			if ( ev && ev.preventDefault ) {
				ev.preventDefault();
			}

			return false;
		});

		return filter;
	}

	function toolbarModuleFilterHtml() {
		var i,
			moduleFilterHtml = "";

		if ( !modulesList.length ) {
			return false;
		}

		modulesList.sort(function( a, b ) {
			return a.localeCompare( b );
		});

		moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
		"<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
		( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
		">< All Modules ></option>";

		for ( i = 0; i < modulesList.length; i++ ) {
			moduleFilterHtml += "<option value='" +
			escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +
			( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +
			">" + escapeText( modulesList[ i ] ) + "</option>";
		}
		moduleFilterHtml += "</select>";

		return moduleFilterHtml;
	}

	function toolbarModuleFilter() {
		var toolbar = id( "qunit-testrunner-toolbar" ),
			moduleFilter = document.createElement( "span" ),
			moduleFilterHtml = toolbarModuleFilterHtml();

		if ( !toolbar || !moduleFilterHtml ) {
			return false;
		}

		moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
		moduleFilter.innerHTML = moduleFilterHtml;

		addEvent( moduleFilter.lastChild, "change", applyUrlParams );

		toolbar.appendChild( moduleFilter );
	}

	function appendToolbar() {
		var toolbar = id( "qunit-testrunner-toolbar" );

		if ( toolbar ) {
			toolbar.appendChild( toolbarUrlConfigContainer() );
			toolbar.appendChild( toolbarLooseFilter() );
		}
	}

	function appendHeader() {
		var header = id( "qunit-header" );

		if ( header ) {
			header.innerHTML = "<a href='" +
			setUrl({ filter: undefined, module: undefined, testId: undefined }) +
			"'>" + header.innerHTML + "</a> ";
		}
	}

	function appendBanner() {
		var banner = id( "qunit-banner" );

		if ( banner ) {
			banner.className = "";
		}
	}

	function appendTestResults() {
		var tests = id( "qunit-tests" ),
			result = id( "qunit-testresult" );

		if ( result ) {
			result.parentNode.removeChild( result );
		}

		if ( tests ) {
			tests.innerHTML = "";
			result = document.createElement( "p" );
			result.id = "qunit-testresult";
			result.className = "result";
			tests.parentNode.insertBefore( result, tests );
			result.innerHTML = "Running...<br />&#160;";
		}
	}

	function storeFixture() {
		var fixture = id( "qunit-fixture" );
		if ( fixture ) {
			config.fixture = fixture.innerHTML;
		}
	}

	function appendUserAgent() {
		var userAgent = id( "qunit-userAgent" );
		if ( userAgent ) {
			userAgent.innerHTML = "";
			userAgent.appendChild( document.createTextNode( navigator.userAgent ) );
		}
	}

	function appendTestsList( modules ) {
		var i, l, x, z, test, moduleObj;

		for ( i = 0, l = modules.length; i < l; i++ ) {
			moduleObj = modules[ i ];

			if ( moduleObj.name ) {
				modulesList.push( moduleObj.name );
			}

			for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {
				test = moduleObj.tests[ x ];

				appendTest( test.name, test.testId, moduleObj.name );
			}
		}
	}

	function appendTest( name, testId, moduleName ) {
		var title, rerunTrigger, testBlock, assertList,
			tests = id( "qunit-tests" );

		if ( !tests ) {
			return;
		}

		title = document.createElement( "strong" );
		title.innerHTML = getNameHtml( name, moduleName );

		rerunTrigger = document.createElement( "a" );
		rerunTrigger.innerHTML = "Rerun";
		rerunTrigger.href = setUrl({ testId: testId });

		testBlock = document.createElement( "li" );
		testBlock.appendChild( title );
		testBlock.appendChild( rerunTrigger );
		testBlock.id = "qunit-test-output-" + testId;

		assertList = document.createElement( "ol" );
		assertList.className = "qunit-assert-list";

		testBlock.appendChild( assertList );

		tests.appendChild( testBlock );
	}

// HTML Reporter initialization and load
	QUnit.begin(function( details ) {
		var qunit = id( "qunit" );

		// Fixture is the only one necessary to run without the #qunit element
		storeFixture();

		if ( qunit ) {
			qunit.innerHTML =
				"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
				"<h2 id='qunit-banner'></h2>" +
				"<div id='qunit-testrunner-toolbar'></div>" +
				"<h2 id='qunit-userAgent'></h2>" +
				"<ol id='qunit-tests'></ol>";
		}

		appendHeader();
		appendBanner();
		appendTestResults();
		appendUserAgent();
		appendToolbar();
		appendTestsList( details.modules );
		toolbarModuleFilter();

		if ( qunit && config.hidepassed ) {
			addClass( qunit.lastChild, "hidepass" );
		}
	});

	QUnit.done(function( details ) {
		var i, key,
			banner = id( "qunit-banner" ),
			tests = id( "qunit-tests" ),
			html = [
				"Tests completed in ",
				details.runtime,
				" milliseconds.<br />",
				"<span class='passed'>",
				details.passed,
				"</span> assertions of <span class='total'>",
				details.total,
				"</span> passed, <span class='failed'>",
				details.failed,
				"</span> failed."
			].join( "" );

		if ( banner ) {
			banner.className = details.failed ? "qunit-fail" : "qunit-pass";
		}

		if ( tests ) {
			id( "qunit-testresult" ).innerHTML = html;
		}

		if ( config.altertitle && defined.document && document.title ) {

			// show âœ– for good, âœ” for bad suite result in title
			// use escape sequences in case file gets loaded with non-utf-8-charset
			document.title = [
				( details.failed ? "\u2716" : "\u2714" ),
				document.title.replace( /^[\u2714\u2716] /i, "" )
			].join( " " );
		}

		// clear own sessionStorage items if all tests passed
		if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {
			for ( i = 0; i < sessionStorage.length; i++ ) {
				key = sessionStorage.key( i++ );
				if ( key.indexOf( "qunit-test-" ) === 0 ) {
					sessionStorage.removeItem( key );
				}
			}
		}

		// scroll back to top to show results
		if ( config.scrolltop && window.scrollTo ) {
			window.scrollTo( 0, 0 );
		}
	});

	function getNameHtml( name, module ) {
		var nameHtml = "";

		if ( module ) {
			nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";
		}

		nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>";

		return nameHtml;
	}

	QUnit.testStart(function( details ) {
		var running, testBlock;

		testBlock = id( "qunit-test-output-" + details.testId );
		if ( testBlock ) {
			testBlock.className = "running";
		} else {

			// Report later registered tests
			appendTest( details.name, details.testId, details.module );
		}

		running = id( "qunit-testresult" );
		if ( running ) {
			running.innerHTML = "Running: <br />" + getNameHtml( details.name, details.module );
		}

	});

	QUnit.log(function( details ) {
		var assertList, assertLi,
			message, expected, actual,
			testItem = id( "qunit-test-output-" + details.testId );

		if ( !testItem ) {
			return;
		}

		message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
		message = "<span class='test-message'>" + message + "</span>";
		message += "<span class='runtime'>@ " + details.runtime + " ms</span>";

		// pushFailure doesn't provide details.expected
		// when it calls, it's implicit to also not show expected and diff stuff
		// Also, we need to check details.expected existence, as it can exist and be undefined
		if ( !details.result && hasOwn.call( details, "expected" ) ) {
			expected = escapeText( QUnit.dump.parse( details.expected ) );
			actual = escapeText( QUnit.dump.parse( details.actual ) );
			message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +
			expected +
			"</pre></td></tr>";

			if ( actual !== expected ) {
				message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
				actual + "</pre></td></tr>" +
				"<tr class='test-diff'><th>Diff: </th><td><pre>" +
				QUnit.diff( expected, actual ) + "</pre></td></tr>";
			}

			if ( details.source ) {
				message += "<tr class='test-source'><th>Source: </th><td><pre>" +
				escapeText( details.source ) + "</pre></td></tr>";
			}

			message += "</table>";

			// this occours when pushFailure is set and we have an extracted stack trace
		} else if ( !details.result && details.source ) {
			message += "<table>" +
			"<tr class='test-source'><th>Source: </th><td><pre>" +
			escapeText( details.source ) + "</pre></td></tr>" +
			"</table>";
		}

		assertList = testItem.getElementsByTagName( "ol" )[ 0 ];

		assertLi = document.createElement( "li" );
		assertLi.className = details.result ? "pass" : "fail";
		assertLi.innerHTML = message;
		assertList.appendChild( assertLi );
	});

	QUnit.testDone(function( details ) {
		var testTitle, time, testItem, assertList,
			good, bad, testCounts, skipped,
			tests = id( "qunit-tests" );

		if ( !tests ) {
			return;
		}

		testItem = id( "qunit-test-output-" + details.testId );

		assertList = testItem.getElementsByTagName( "ol" )[ 0 ];

		good = details.passed;
		bad = details.failed;

		// store result when possible
		if ( config.reorder && defined.sessionStorage ) {
			if ( bad ) {
				sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );
			} else {
				sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name );
			}
		}

		if ( bad === 0 ) {
			addClass( assertList, "qunit-collapsed" );
		}

		// testItem.firstChild is the test name
		testTitle = testItem.firstChild;

		testCounts = bad ?
		"<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " :
			"";

		testTitle.innerHTML += " <b class='counts'>(" + testCounts +
		details.assertions.length + ")</b>";

		if ( details.skipped ) {
			testItem.className = "skipped";
			skipped = document.createElement( "em" );
			skipped.className = "qunit-skipped-label";
			skipped.innerHTML = "skipped";
			testItem.insertBefore( skipped, testTitle );
		} else {
			addEvent( testTitle, "click", function() {
				toggleClass( assertList, "qunit-collapsed" );
			});

			testItem.className = bad ? "fail" : "pass";

			time = document.createElement( "span" );
			time.className = "runtime";
			time.innerHTML = details.runtime + " ms";
			testItem.insertBefore( time, assertList );
		}
	});

	if ( !defined.document || document.readyState === "complete" ) {
		config.pageLoaded = true;
		config.autorun = true;
	}

	if ( defined.document ) {
		addEvent( window, "load", QUnit.load );
	}

})();